﻿
using Game2DFramework.Extensions;
using MechGame.Game.PlayerRobot;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MechGame.Game.Leveling
{
    enum DoorState
    {
        Closed,
        Opening,
        Opened,
        OpenedWaitForLeave,
        Catching,
        Closing,
    }

    abstract class DoorBase
    {
        private float _currentPercentageDoorClosed;
        private readonly Texture2D _texture;
        private readonly float _doorSpeed;
        private readonly float _reactionRadius;

        public Vector2 Position;
        public Vector2 CatchPosition;

        protected DoorBase(Texture2D texture, float doorSpeed, float reactionRadius)
        {
            _texture = texture;
            State = DoorState.Closed;
            _currentPercentageDoorClosed = 1;
            _doorSpeed = doorSpeed;
            _reactionRadius = reactionRadius;
        }

        public bool IsPlayerCatched { get; private set; }
        public DoorState State { get; private set; }

        public virtual bool AdditionalOpenCondition(Player player)
        {
            return true;
        }
        
        public void Update(float elapsed, Player player)
        {
            switch (State)
            {
                case DoorState.Opened:
                    UpdateOpened(player);
                    break;
                case DoorState.Closed:
                    UpdateClosed(player);
                    break;
                case DoorState.OpenedWaitForLeave:
                    UpdateOpenedWaitForLeave(player);
                    break;
                case DoorState.Catching:
                    UpdateCatching(elapsed, player);
                    break;
                case DoorState.Opening:
                    UpdateOpening(elapsed);
                    break;
                case DoorState.Closing:
                    UpdateClosing(elapsed);
                    break;
            }
        }

        private void UpdateClosed(Player player)
        {
            if (!IsPlayerCatched && PlayerInReactionRange(player) && AdditionalOpenCondition(player)) Open();
        }

        private bool PlayerInReactionRange(Player player)
        {
            return (player.Position - (Position + _texture.GetCenter())).LengthSquared() <= _reactionRadius * _reactionRadius;
        }

        private void UpdateOpenedWaitForLeave(Player player)
        {
            if (!player.Bounds.Intersects(new Rectangle((int)Position.X, (int)Position.Y, _texture.Width, _texture.Height)))
            {
                State = DoorState.Opened;
                IsPlayerCatched = false;
            }
        }

        public void ReleasePlayer()
        {
            Open();
        }

        public void SetInCatchedMode(Player player)
        {
            player.Position = CatchPosition;
            State = DoorState.Closed;
            IsPlayerCatched = true;
            _currentPercentageDoorClosed = 1;
        }

        private void UpdateCatching(float elapsed, Player player)
        {
            var centeredPosition = Position + _texture.GetCenter();
            if (player.Position == centeredPosition)
            {
                IsPlayerCatched = true;
                Close();
            }
            else
            {
                player.MoveToPosition(elapsed, centeredPosition);
            }
        }

        private void UpdateOpened(Player player)
        {
            if (!IsPlayerCatched)
            {
                if (PlayerInCatchableRange(player))
                {
                    State = DoorState.Catching;
                    player.LockControl = true;      
                }
                else if (!PlayerInReactionRange(player))
                {
                    Close();
                }
            }
            else
            {
                State = DoorState.OpenedWaitForLeave;
                player.LockControl = false;
            }
        }

        private bool PlayerInCatchableRange(Player player)
        {
            return player.Bounds.Intersects(new Rectangle((int)Position.X, (int)Position.Y, _texture.Width, _texture.Height));
        }

        private void UpdateClosing(float elapsed)
        {
            _currentPercentageDoorClosed += _doorSpeed * elapsed;
            if (_currentPercentageDoorClosed >= 1)
            {
                _currentPercentageDoorClosed = 1;
                State = DoorState.Closed;
            }
        }

        private void UpdateOpening(float elapsed)
        {
            _currentPercentageDoorClosed -= _doorSpeed * elapsed;
            if (_currentPercentageDoorClosed <= 0)
            {
                _currentPercentageDoorClosed = 0;
                State = DoorState.Opened;
            }
        }

        private void Open()
        {
            if (State == DoorState.Closed || State == DoorState.Catching) State = DoorState.Opening;
        }

        private void Close()
        {
            if (State == DoorState.Opened || State == DoorState.Catching) State = DoorState.Closing;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            var delta = MathHelper.SmoothStep(0, 1, _currentPercentageDoorClosed);

            var halfWidth = _texture.Width / 2;
            var doorWidth = (int)(delta * halfWidth);
            var leftSource = new Rectangle(halfWidth - doorWidth, 0, doorWidth, _texture.Height);
            var rightSource = new Rectangle(halfWidth, 0, doorWidth, _texture.Height);

            spriteBatch.Draw(_texture, Position, leftSource, Color.White);
            spriteBatch.Draw(_texture, Position + new Vector2(_texture.Width - doorWidth, 0), rightSource, Color.White);
        }
    }
}
